#import "MainWindowController.h"
#import "NSManagedObjectContext+SRAdditions.h"
#import "NSMutableArray+SRAdditions.h"
#import "NSTreeController+SRAdditions.h"
#import "SourceListViewController.h"
#import "RNToolbarViewController.h"
#import "RNPreferencesController.h"
#import "DetailViewController.h"
#import "RNNewItemSheetController.h"
#import "RNConstants.h"
#import "RNSearch.h"
#import "RNList.h"
#import "RNItem.h"
#import "RNListItem.h"
#import "RNAisle.h"
#import "RNCategory.h"
#import "RNCatalog.h"
#import "RNToolbarButton.h"

#pragma mark -
@interface ToolbarDisplayStatusTitleTransformer : NSValueTransformer {} @end
@implementation ToolbarDisplayStatusTitleTransformer

+(Class)transformedValueClass {
    return [NSString class];
}
+(BOOL)allowsReverseTransformation {
    return NO;
}
-(id)transformedValue:(id)value {
    return [value boolValue] ? @"Show Toolbar" : @"Hide Toolbar"; 
}
@end


#pragma mark -
#pragma mark Constants
static NSColor *RNActiveMainSplitViewDividerColor;
static NSColor *RNInactiveMainSplitViewDividerColor;
static NSGradient *NavigationBarBackgroundGradient;


#pragma mark -
@interface MainWindowController ()
//@property (nonatomic, retain) id rootObject;
@property (nonatomic, retain) NSMutableArray *previousRootObjects;
@property (nonatomic, retain) NSMutableArray *nextRootObjects;
@end


#pragma mark -
@interface MainWindowController (MainWindowControllerPrivateMethods)
-(void)adjustDetailFrameToAccommodateToolbar;
@end

@implementation MainWindowController

@synthesize sourceListContentView = sourceListContentView_;
@synthesize detailContentView = detailContentView_;
@synthesize mainSplitView = mainSplitView_;
@synthesize mainSplitRightView = mainSplitRightView_;
@synthesize sourceListViewController = sourceListViewController_;
@synthesize detailViewController = detailViewController_;
@synthesize newItemSheetController = newItemSheetController_;
@synthesize newListItemSheetController = newListItemSheetController_;
@synthesize applicationSearchMenu = applicationSearchMenu_;
@synthesize searchField = searchField_;
@synthesize toolbar = toolbar_;
@synthesize toolbarViewController = toolbarViewController_;
@synthesize rootObject = rootObject_;
@synthesize previousRootObjects = previousRootObjects_;
@synthesize nextRootObjects = nextRootObjects_;
//@synthesize previousButton = previousButton_;
//@synthesize nextButton = nextButton_;
@synthesize pathControl = pathControl_;
@synthesize navBar = navBar_;
@synthesize storeFilterPopUp = storeFilterPopUp_;
@synthesize storeFilterArrayController = storeFilterArrayController_;
@synthesize preferencesController = preferencesController_;
//@synthesize completedItemsTimer = completedItemsTimer_;
@synthesize incompleteListItemsArrayController = incompleteListItemsArrayController_;
@synthesize incompleteListItemsFilterPredicate = incompleteListItemsFilterPredicate_;

+(void)initialize {
    RNActiveMainSplitViewDividerColor = [NSColor colorWithCalibratedWhite:0.251f alpha:1.0f];
    RNInactiveMainSplitViewDividerColor = [NSColor colorWithCalibratedWhite:0.529f alpha:1.0f];
    NavigationBarBackgroundGradient = [[NSGradient alloc] initWithStartingColor:[NSColor colorWithCalibratedRed:0.611f green:0.635f blue:0.694f alpha:1.000f] endingColor:[NSColor colorWithCalibratedRed:0.571f green:0.594f blue:0.649f alpha:1.000f]];
}

-(id)initWithWindowNibName:(NSString *)nibName managedObjectContext:(NSManagedObjectContext *)moc {
    if (!(self = [super initWithWindowNibName:nibName managedObjectContext:moc]))
        return nil;
    
    [self setPreviousRootObjects:[NSMutableArray array]];
    [self setNextRootObjects:[NSMutableArray array]];
    
    [self setSourceListViewController:[[SourceListViewController alloc] initWithNibName:@"SourceListView"
                                                                                           bundle:nil
                                                                                 windowController:self
                                                                             managedObjectContext:[[NSApp delegate] managedObjectContext]]];
    [self addViewController:[self sourceListViewController]];
    
    [self setDetailViewController:[[DetailViewController alloc] initWithNibName:@"DetailView" 
                                                                         bundle:nil
                                                               windowController:self 
                                                           managedObjectContext:[[NSApp delegate] managedObjectContext]]];
    [self addViewController:[self detailViewController]];
    [NSValueTransformer setValueTransformer:[[ToolbarDisplayStatusTitleTransformer alloc] init] forName:@"ToolbarDisplayStatusTitleTransformer"];

    return self;
}

-(void)awakeFromNib {
    [[self window] setExcludedFromWindowsMenu:YES];
    [self adjustDetailFrameToAccommodateToolbar];
    NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
    [defaultCenter addObserver:self selector:@selector(managedObjectsDidChange:) name:NSManagedObjectContextObjectsDidChangeNotification object:[self managedObjectContext]];
//    [defaultCenter addObserver:self selector:@selector(toolbarFrameChanged:) name:NSViewFrameDidChangeNotification object:[self toolbar]];
    [[self toolbar] addObserver:self forKeyPath:@"showsTitles" options:NSKeyValueObservingOptionNew context:NULL];
    [defaultCenter addObserver:self selector:@selector(applicationDidBecomeActive:) name:NSApplicationDidBecomeActiveNotification object:nil];
    [defaultCenter addObserver:self selector:@selector(applicationDidResignActive:) name:NSApplicationDidResignActiveNotification object:nil];
    [defaultCenter addObserver:self selector:@selector(detailViewDidChangeSelection:) name:NSOutlineViewSelectionDidChangeNotification object:[[self detailViewController] detailView]];
    [[self detailViewController] addObserver:self forKeyPath:@"storeFilter" options:NSKeyValueObservingOptionNew context:NULL];
    [[self mainSplitView] setValue:[NSNumber numberWithBool:YES] forKey:@"colorIsEnabled"];
    
    NSView *detailView = [[self detailViewController] view];
    [detailView setFrame:[[self detailContentView] bounds]];
    [[self detailContentView] addSubview:detailView];

    NSView *sourceListView = [[self sourceListViewController] view];
    [sourceListView setFrame:[[self sourceListContentView] bounds]];
    [[self sourceListContentView] addSubview:sourceListView];
    [defaultCenter addObserver:self selector:@selector(sourceListSelectionDidChange:) name:NSOutlineViewSelectionDidChangeNotification object:[[self sourceListViewController] sourceList]];
    
    NSInteger applicationSearchType = [[NSUserDefaults standardUserDefaults] integerForKey:RNApplicationSearchTypeKey];
    if (applicationSearchType == 0)
        applicationSearchType = 1;
    [[[self applicationSearchMenu] itemWithTag:applicationSearchType] setState:NSOnState];
    [[RNSearch sharedSearch] setApplicationSearchType:applicationSearchType];
    
    if ([[NSUserDefaults standardUserDefaults] boolForKey:RNHideToolbarKey])
        [self toggleShowToolbar:nil];
    
    NSNumber *dockCountDisplay = [[NSUserDefaults standardUserDefaults] objectForKey:@"dockCountDisplay"];
    NSUInteger dockCountDisplayType = [dockCountDisplay integerValue];
    NSPredicate *listItemsFilterPredicate = nil;
    if (dockCountDisplayType == 1)
        listItemsFilterPredicate = [NSPredicate predicateWithFormat:@"complete == %@", [NSNumber numberWithBool:NO]];
    [self setIncompleteListItemsFilterPredicate:listItemsFilterPredicate];
//    if (dockCountDisplayType != 0) {
        [[self incompleteListItemsArrayController] addObserver:self forKeyPath:@"arrangedObjects" options:NSKeyValueObservingOptionNew context:NULL];
//    }
    [[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.dockCountDisplay" options:NSKeyValueObservingOptionNew context:NULL];
}

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    // This is for the dock display preference
    if ([keyPath isEqualToString:@"values.dockCountDisplay"]) {
        NSUInteger dockCountDisplay = [[NSUserDefaults standardUserDefaults] integerForKey:@"dockCountDisplay"];
        NSPredicate *listItemsFilterPredicate = nil;
        if (dockCountDisplay == 0) 
            [[NSApp dockTile] setBadgeLabel:@""];
        else if (dockCountDisplay == 1)
            listItemsFilterPredicate = [NSPredicate predicateWithFormat:@"complete == %@", [NSNumber numberWithBool:NO]];
        else if (dockCountDisplay == 2 && [[self rootObject] isKindOfClass:[RNList class]])
            listItemsFilterPredicate = [NSPredicate predicateWithFormat:@"complete == %@ AND list == %@", [NSNumber numberWithBool:NO], [self rootObject]];
        [self setIncompleteListItemsFilterPredicate:listItemsFilterPredicate];
        return;
    }
    // This is the incompleteListItemsArrayController used to track items for the app icon badge
    if ([keyPath isEqualToString:@"arrangedObjects"]) {
        NSUInteger dockCount = [[NSUserDefaults standardUserDefaults] integerForKey:@"dockCountDisplay"];
        if (dockCount == 0)
            return;
        NSDockTile *dockTile = [NSApp dockTile];
        if (dockCount == 2 && ![[self rootObject] isKindOfClass:[RNList class]]) {
            [dockTile setBadgeLabel:@""];
            return;
        }
        NSInteger badgeCount = [[[self incompleteListItemsArrayController] arrangedObjects] count];
        [dockTile setBadgeLabel:badgeCount > 0 ? [NSString stringWithFormat:@"%i", badgeCount] : @""];
        return;
    }
    if ([keyPath isEqualToString:@"showsTitles"]) {
        [self adjustDetailFrameToAccommodateToolbar];
        return;
    }
    // Monitors changes in the detail view controller's store filter and resizes the nav bar components to fit
    [[self storeFilterPopUp] synchronizeTitleAndSelectedItem];
    CGFloat maxX = NSMaxX([[self storeFilterPopUp] frame]);
    [[self storeFilterPopUp] sizeToFit];
    NSRect storeFilterFrame = [[self storeFilterPopUp] frame];
    storeFilterFrame.origin.x = maxX - NSWidth(storeFilterFrame);
    [[self storeFilterPopUp] setFrame:storeFilterFrame];
}

- (NSUndoManager *)windowWillReturnUndoManager:(NSWindow *)window {
    return [[self managedObjectContext] undoManager];
}

-(NSArray *)storesControllerSortDescriptors {
    return [NSArray arrayWithObject:[[NSSortDescriptor alloc] initWithKey:@"title" ascending:YES]];
}

-(RNPreferencesController *)preferencesController {
    if (!preferencesController_)
        preferencesController_ = [[RNPreferencesController alloc] initWithWindowNibName:@"Preferences"];
    return preferencesController_;
}

-(BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)anItem {
    SEL action = [anItem action];
    if (action == @selector(toggleFavoriteForSelectedItems:)) {
        NSIndexSet *selectedIndexes = [[[self detailViewController] detailView] selectedRowIndexes];
        __block BOOL returnValue = YES, markAsFavorite = NO;
        if ([selectedIndexes count] == 0) {
            markAsFavorite = YES;
            return NO;
        }
        [selectedIndexes enumerateIndexesUsingBlock:^(NSUInteger selectedIndex, BOOL *stop) { 
            id object = [[[[self detailViewController] detailView] itemAtRow:selectedIndex] representedObject];
            if (![object isKindOfClass:[RNItem class]] && ![object isKindOfClass:[RNListItem class]]) {
                markAsFavorite = YES;
                returnValue = NO;
            }
            else if ([[object favorite] boolValue] == NO) {
                markAsFavorite = YES;
            }

        }];
        if ([(id)anItem isKindOfClass:[NSMenuItem class]])
            [(NSMenuItem *)anItem setTitle:markAsFavorite ? @"Mark As Favorite" : @"Mark As Not Favorite"];
        else if ([(id)anItem isKindOfClass:[RNToolbarButton class]] && [(RNToolbarButton *)anItem imagePosition] != NSImageOnly) {
            [(RNToolbarButton *)anItem setTitle:markAsFavorite ? @"Favorite" : @"Not Favorite"];
            [[[self toolbar] layoutHelper] tile];
        }
        return returnValue;
    }
    return YES;
}

-(void)setRootObject:(id)newRootObject {
    rootObject_ = newRootObject;
    if (![newRootObject isKindOfClass:[RNSearch class]]) {
        [[self searchField] setStringValue:@""];
        [[self window] makeFirstResponder:(NSResponder *)[[self detailViewController] detailView]];
    }
    NSNumber *dockCountDisplay = [[NSUserDefaults standardUserDefaults] objectForKey:@"dockCountDisplay"];
    NSUInteger dockCountDisplayType = [dockCountDisplay integerValue];
    if (dockCountDisplayType == 2) {
        BOOL rootIsList = [newRootObject isKindOfClass:[RNList class]];
        [self setIncompleteListItemsFilterPredicate:rootIsList ? [NSPredicate predicateWithFormat:@"complete == %@ AND list == %@", [NSNumber numberWithBool:NO], newRootObject] : nil];
    }
}


#pragma mark -
#pragma mark Notifications

-(void)managedObjectsDidChange:(NSNotification *)note {
    NSSet *deleted = [[note userInfo] valueForKey:NSDeletedObjectsKey];
    if ([deleted count] == 0)
        return;
    [self willChangeValueForKey:@"previousRootObject"];
    [[self previousRootObjects] removeObjectsInArray:[deleted allObjects]];
    [self didChangeValueForKey:@"previousRootObject"];

    [self willChangeValueForKey:@"nextRootObject"];
    [[self nextRootObjects] removeObjectsInArray:[deleted allObjects]];
    [self didChangeValueForKey:@"nextRootObject"];
}

-(void)applicationDidBecomeActive:(NSNotification *)note {
    [[self mainSplitView] setValue:RNActiveMainSplitViewDividerColor forKey:@"color"];
}

-(void)applicationDidResignActive:(NSNotification *)note {
    [[self mainSplitView] setValue:RNInactiveMainSplitViewDividerColor forKey:@"color"];
}

-(void)adjustDetailFrameToAccommodateToolbar {
    CGFloat contentHeight = NSHeight([[self mainSplitRightView] frame]);
    CGFloat toolbarHeight = [[self toolbar] superview] != nil ? NSHeight([[self toolbar] frame]) : 0;
    CGFloat navBarHeight = NSHeight([[self navBar] frame]);
    CGFloat detailViewHeight = contentHeight - toolbarHeight - navBarHeight;
    NSView *detailContentView = [self detailContentView];
    NSRect detailContentViewRect = [detailContentView frame];
    CGFloat deltaHeight = detailViewHeight - NSHeight(detailContentViewRect);
    detailContentViewRect.size.height += deltaHeight;
    detailContentViewRect.origin.y -= deltaHeight;
    [detailContentView setFrame:detailContentViewRect];
}

-(void)sourceListSelectionDidChange:(NSNotification *)note {
    NSArray *selectedObjects = [[[self sourceListViewController] sourceListTreeController] selectedObjects];
    id selectedObject = [selectedObjects count] > 0 ? [selectedObjects objectAtIndex:0] : nil;
    [[self previousRootObjects] push:[self rootObject]];
    [[self nextRootObjects] removeAllObjects];
    [self setRootObject:selectedObject];
}

-(void)detailViewDidChangeSelection:(NSNotification *)note {
    [[self toolbar] validateVisibleItems];
}


#pragma mark -
#pragma mark Actions

-(IBAction)toggleShowToolbar:(id)sender {
    if ([[self toolbar] superview])
        [[self toolbar] removeFromSuperview];
    else
        [[self mainSplitRightView] addSubview:(NSView *)[self toolbar]];
//    [self toolbarFrameChanged:nil];
    [self adjustDetailFrameToAccommodateToolbar];
    [[NSUserDefaults standardUserDefaults] setBool:[[self toolbar] superview] == nil forKey:RNHideToolbarKey];
}

-(IBAction)toggleFavoriteForSelectedItems:(id)sender {
    BOOL markAsFavorite = NO;
    NSArray *selectedItems = [[[self detailViewController] detailTreeController] selectedObjects];
    NSArray *unfavorites = [selectedItems filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"favorite == %@", [NSNumber numberWithBool:NO]]];
    if ([unfavorites count] > 0)
        markAsFavorite = YES;
    [selectedItems makeObjectsPerformSelector:@selector(setFavorite:) withObject:[NSNumber numberWithBool:markAsFavorite]];
//    [[self toolbarViewController] updateFavoriteToolbarItem];
}

//-(void)removeObject:(NSManagedObject *)object {
//    NSUndoManager *undoManager = [[self window] undoManager];
//    [[undoManager prepareWithInvocationTarget:self] insertObject:object withProperties:(];
//    [[self managedObjectContext] deleteObject:object];
//}
//
//-(void)insertObject:(NSManagedObject *)object withProperties:(NSDictionary *)properties {
//    NSUndoManager *undoManager = [[self window] undoManager];
//    [[undoManager prepareWithInvocationTarget:self] removeObject:object];
//    [[self managedObjectContext] insertObject:object];
//    [object setValuesForKeysWithDictionary:properties];
//}

-(IBAction)applicationSearch:(id)sender {
    [[self window] makeFirstResponder:[self searchField]];
}

-(id)sourceListObjectForRootObject {
    id sourceListObject = [self rootObject];
    if ([sourceListObject isKindOfClass:[RNCategory class]]) 
        sourceListObject = [RNCatalog sharedCatalog];
    else if ([sourceListObject isKindOfClass:[RNAisle class]]) 
        sourceListObject = [sourceListObject store];
    else if ([sourceListObject isKindOfClass:[RNSearch class]])
        sourceListObject = nil;
    return sourceListObject;
}

-(void)syncSourceListWithRootObject {
    NSNotificationCenter *noteCenter = [NSNotificationCenter defaultCenter];
    
    // Temporarily stop notifcations of changes to the source list while we change it
    [noteCenter removeObserver:self name:NSOutlineViewSelectionDidChangeNotification object:[[self sourceListViewController] sourceList]];
    NSTreeNode *selectionNode = [[[self sourceListViewController] sourceListTreeController] treeNodeForObject:[self sourceListObjectForRootObject]];
    [[[self sourceListViewController] sourceListTreeController] setSelectionIndexPath:[selectionNode indexPath]];
    [noteCenter addObserver:self selector:@selector(sourceListSelectionDidChange:) name:NSOutlineViewSelectionDidChangeNotification object:[[self sourceListViewController] sourceList]];
}

-(void)selectRootObject:(id)newRootObject {
    [[self previousRootObjects] push:[self rootObject]];
    [[self nextRootObjects] removeAllObjects];
    [self setRootObject:newRootObject];
    [self syncSourceListWithRootObject];
}

-(IBAction)selectPreviousRootObject:(id)sender {
    [[self nextRootObjects] push:[self rootObject]];
    [self setRootObject:[[self previousRootObjects] pop]];
    [self syncSourceListWithRootObject];
}

-(IBAction)selectNextRootObject:(id)sender {
    [[self previousRootObjects] push:[self rootObject]];
    [self setRootObject:[[self nextRootObjects] pop]];
    [self syncSourceListWithRootObject];
}

+(NSSet *)keyPathsForValuesAffectingPreviousRootObject {
    return [NSSet setWithObject:@"rootObject"];
}

-(id)previousRootObject {
    return [[self previousRootObjects] peek];
}

+(NSSet *)keyPathsForValuesAffectingNextRootObject {
    return [NSSet setWithObject:@"rootObject"];
}

-(id)nextRootObject {
    return [[self nextRootObjects] peek];
}

-(IBAction)applicationPreferences:(id)sender {
    [[self preferencesController] showPreferences];
}

-(void)autologCompletedItems {
    NSNumber *loggingIntervalNumber = [[NSUserDefaults standardUserDefaults] objectForKey:RNLogCompletedItemsIntervalKey];
    RNLogCompletedItemsInterval loggingInterval = loggingIntervalNumber == nil ? 1 : [loggingIntervalNumber integerValue];

    if (loggingInterval == RNLogCompletedItemsImmediately || loggingInterval == RNLogCompletedItemsManually)
        return;
    
    NSInteger daysToAdd = 1;
    if (loggingInterval == RNLogCompletedItemsWeekly)
        daysToAdd = 7;
    else if (loggingInterval == RNLogCompletedItemsMonthly)
        daysToAdd = 30;
    
    NSDate *lastDateLogged = [[NSUserDefaults standardUserDefaults] objectForKey:RNLogCompletedItemsDateKey];
    NSCalendar *currentCalendar = [NSCalendar currentCalendar];
    NSDateComponents *addDays = [[NSDateComponents alloc] init];
    [addDays setDay:daysToAdd];
    NSDate *nextLoggingDate = (lastDateLogged == nil) ? [NSDate distantPast] : [currentCalendar dateByAddingComponents:addDays toDate:lastDateLogged options:0];
    if ([[NSDate date] earlierDate:nextLoggingDate] == nextLoggingDate) 
        [[self detailViewController] clearCompletedItemsInAllLists];
}

-(void)autoClearHistoryItems {
    NSNumber *clearHistoryItemsIntervalComponent = [[NSUserDefaults standardUserDefaults] objectForKey:RNClearHistoryIntervalComponentKey];
    NSNumber *clearHistoryItemsIntervalAmount = [[NSUserDefaults standardUserDefaults] objectForKey:RNClearHistoryIntervalAmountKey];
    if ([clearHistoryItemsIntervalAmount integerValue] == -1 /* keep forever */)
        return;
    NSInteger keepDays = -1 * [clearHistoryItemsIntervalAmount integerValue] * [clearHistoryItemsIntervalComponent integerValue];
    NSDateComponents *addDays = [[NSDateComponents alloc] init];
    [addDays setDay:keepDays];
    NSCalendar *currentCalendar = [NSCalendar currentCalendar];
    NSDate *expireDate = [currentCalendar dateByAddingComponents:addDays toDate:[NSDate date] options:0];
    NSArray *historyItems = [[self managedObjectContext] fetchObjectsForEntityName:RNEntityNameHistoryItem withPredicate:@"loggedDate < %@", expireDate];
    [[self managedObjectContext] deleteObjects:historyItems];
}


#pragma mark -
#pragma mark New Item Sheet Methods

-(RNNewItemSheetController *)newItemSheetController {
    if (!newItemSheetController_) 
        newItemSheetController_ = [[RNNewItemSheetController alloc] initWithWindowNibName:@"NewItemSheet" managedObjectContext:[self managedObjectContext]];
    return newItemSheetController_;
}

-(IBAction)displayNewItemSheet:(id)sender {
    [[self newItemSheetController] displayNewItemSheet];
}

-(IBAction)displayNewItemSheetAndSelectItem:(id)sender {
    [[self newItemSheetController] selectItemWithTag:RNNewItemSheetItemToolbarItem];
    [[self newItemSheetController] displayNewItemSheet];
}

-(IBAction)displayNewItemSheetAndSelectAisle:(id)sender {
    [[self newItemSheetController] selectItemWithTag:RNNewItemSheetAisleToolbarItem];
    [[self newItemSheetController] displayNewItemSheet];
}

-(IBAction)displayNewItemSheetAndSelectCategory:(id)sender {
    [[self newItemSheetController] selectItemWithTag:RNNewItemSheetCategoryToolbarItem];
    [[self newItemSheetController] displayNewItemSheet];
}

-(IBAction)displayNewItemSheetAndSelectList:(id)sender {
    [[self newItemSheetController] selectItemWithTag:RNNewItemSheetListToolbarItem];
    [[self newItemSheetController] displayNewItemSheet];
}

@end
